package com.tian.service.impl;

import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.tian.config.RedisConfig;
import com.tian.dto.PayNotifyReqDto;
import com.tian.dto.WxPrePayReqDto;
import com.tian.entity.*;
import com.tian.enums.*;
import com.tian.mapper.*;
import com.tian.service.PayService;
import com.tian.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2023年05月12日 18:06
 * 博客地址：<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 微信支付
 */
@Slf4j
@Service
public class PayServiceImpl implements PayService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ChargerRecordMapper chargerRecordMapper;
    @Resource
    private PayOrderMapper payOrderMapper;
    @Resource
    private UserActivityCouponMapper userActivityCouponMapper;
    @Resource
    private CouponMapper couponMapper;
    @Resource
    private RedisConfig redisConfig;
    @Resource
    private UserMapper userMapper;

    @Override
    public CommonResult<WxPayMpOrderResult> prePay(WxPrePayReqDto wxPrePayReqDto, HttpServletRequest request) {
        wxPrePayReqDto.setIp(HttpRequestUtil.getIpAddr(request));
        String orderNo = ConstantUtil.ORDER_PRE + redisConfig.incr(RedisConstantPre.ORDER_NO_KEY);
        String key = null;
        try {
            wxPrePayReqDto.setOrderNo(orderNo);
            PayOrder payOrder = new PayOrder();
            Coupon coupon = null;
            BigDecimal couponAmount = null;
            if (this.needLock(wxPrePayReqDto.getUserCouponId())) {
                key = RedisConstantPre.USE_USER_COUPON_PRE + wxPrePayReqDto.getUserCouponId();
                RLock rLock = redissonClient.getLock(key);
                rLock.lock();
                UserActivityCoupon userActivityCoupon = userActivityCouponMapper.selectByPrimaryKey(wxPrePayReqDto.getUserCouponId());
                userActivityCoupon.setStatus(UserCouponStatusEnum.USE.getStatus());
                userActivityCouponMapper.updateByPrimaryKey(userActivityCoupon);

                payOrder.setUserCouponId(wxPrePayReqDto.getUserCouponId());

                coupon = couponMapper.selectByPrimaryKey(userActivityCoupon.getCouponId());

                if (CouponTypeEnum.AMOUNT.getType() == coupon.getCouponType()) {
                    couponAmount = coupon.getCouponValue();
                } else {
                    couponAmount = wxPrePayReqDto.getAmount().multiply(coupon.getCouponValue());
                }
            }

            User user = UserCacheUtil.getUser();
            ChargerRecord chargerRecord = chargerRecordMapper.selectByUserId(user.getId());

            payOrder.setCreateTime(new Date());
            BigDecimal payAmount = chargerRecord.getMoney();
            if (coupon != null) {
                payAmount = payAmount.subtract(couponAmount);
            }
            payOrder.setMoney(wxPrePayReqDto.getAmount());
            payOrder.setOrderNo(orderNo);
            payOrder.setChargerRecordId(chargerRecord.getId());
            payOrder.setBusinessType(PayBusinessTypeEnum.PAY_CHARGER.getBusiness());
            payOrder.setStatus(WxPayStatusEnum.INIT.getStatus());

            payOrderMapper.insert(payOrder);

            chargerRecord.setPayStatus(PayStatusEnum.UN_PAY.getPayStatus());
            chargerRecordMapper.updateByPrimaryKey(chargerRecord);

            WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
            orderRequest.setOpenid(wxPrePayReqDto.getOpenId());
            orderRequest.setOutTradeNo(orderNo);
            orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
            orderRequest.setSpbillCreateIp(wxPrePayReqDto.getIp());
            orderRequest.setTotalFee(BaseWxPayRequest.yuanToFen(payAmount.toPlainString()));
            orderRequest.setBody("充电付款");
            orderRequest.setNonceStr(String.valueOf(System.currentTimeMillis()));
            com.github.binarywang.wxpay.service.WxPayService wxService = new WxPayServiceImpl();
            WxPayMpOrderResult wxPayMpOrderResult = wxService.createOrder(orderRequest);
            return CommonResult.success(wxPayMpOrderResult);
        } catch (WxPayException ex) {
            log.error("生成预付单失败：", ex);
            return CommonResult.failed(ResultCode.FAILED);
        } finally {
            if (key != null) {
                RLock rLock = redissonClient.getLock(key);
                rLock.unlock();
            }
        }
    }

    private boolean needLock(Integer userCouponId) {
        return userCouponId != null && userCouponId != 0;
    }

    @Override
    public String payNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            com.github.binarywang.wxpay.service.WxPayService wxService = new WxPayServiceImpl();
            WxPayOrderNotifyResult result = wxService.parseOrderNotifyResult(xmlResult);
            // 结果正确
            String orderNo = result.getOutTradeNo();
            String tradeNo = result.getTransactionId();
            String resultCode = result.getResultCode();
            if (StringUtil.isEmpty(orderNo) || StringUtil.isBlank(tradeNo)) {
                return getResultMsg("FAILED", "支付回调通知参数为空", response);
            }
            PayNotifyReqDto payNotifyReqDto = new PayNotifyReqDto();
            payNotifyReqDto.setOrderNo(orderNo);
            payNotifyReqDto.setThirdTradeNo(tradeNo);
            payNotifyReqDto.setResultCode(resultCode);

            PayOrder payOrder = payOrderMapper.selectByOrderNo(orderNo);

            if (payOrder == null) {
                log.error("支付回调通知，orderNo={} 不存在", orderNo);
                return getResultMsg("FAILED", "支付回调通知，orderNo=" + orderNo + " 不存在", response);
            }

            ChargerRecord chargerRecord = chargerRecordMapper.selectByPrimaryKey(payOrder.getChargerRecordId());

            //分布式锁锁住该用户账号---
            String key = RedisConstantPre.BALANCE_LOCK_PRE + orderNo;
            RLock rLock = redissonClient.getLock(key);
            try {
                rLock.lock();
                User user = userMapper.getUserById(payOrder.getChargerRecordId());

                if (WxPayConstants.ResultCode.SUCCESS.equals(payNotifyReqDto.getResultCode())) {
                    //修改状态
                    payOrder.setPayOrder(payNotifyReqDto.getThirdTradeNo());
                    payOrder.setStatus(WxPayStatusEnum.SUCCESS.getStatus());
                    payOrderMapper.updateByPrimaryKey(payOrder);

                    if (payOrder.getChargerRecordId() == null) {
                        if (payOrder.getUserCouponId() != null) {
                            UserActivityCoupon userActivityCoupon = userActivityCouponMapper.selectByPrimaryKey(payOrder.getUserCouponId());
                            userActivityCoupon.setUsedTime(new Date());
                            userActivityCouponMapper.updateByPrimaryKey(userActivityCoupon);
                        }
                        BigDecimal newMoney = user.getMoney().add(payOrder.getMoney());
                        //给用户账户添加余额
                        user.setMoney(newMoney);
                        userMapper.updateUser(user);
                    } else {
                        chargerRecord.setPayStatus(PayStatusEnum.PAY_FINISH.getPayStatus());
                        chargerRecordMapper.updateByPrimaryKey(chargerRecord);
                    }

                    return getResultMsg("SUCCESS", "OK", response);
                }

                if (WxPayConstants.ResultCode.FAIL.equals(payNotifyReqDto.getResultCode())) {
                    payOrder.setPayOrder(payNotifyReqDto.getThirdTradeNo());
                    payOrder.setStatus(WxPayStatusEnum.FAILED.getStatus());
                    payOrderMapper.updateByPrimaryKey(payOrder);
                    return getResultMsg("SUCCESS", "OK", response);
                }

                return getResultMsg("SUCCESS", "OK", response);
            } finally {
                rLock.unlock();
            }
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因{}", e.getMessage());
            HashMap<Object, Object> map = new HashMap<>();
            map.put("return_code", "FAILED");
            map.put("return_msg", e.getMessage());
            String resultXml = XmlUtil.mapToXml(map, false);
            response.setContentType("text/xml");
            log.info("[支付_微信支付]通知已处理");
            return resultXml;
        }
    }

    private String getResultMsg(String returnCode, String returnMsg, HttpServletResponse response) {
        //支付成功，回传通知微信已收到交易成功通知
        HashMap<Object, Object> map = new HashMap<>();
        map.put("return_code", returnCode);
        map.put("return_msg", returnMsg);
        String resultXml = XmlUtil.mapToXml(map, false);
        response.setContentType("text/xml");
        log.info("[支付_微信支付]通知已处理");
        return resultXml;
    }
}
